from emi import report
from emi.bar.data import Interval, BarData, CategoryMeta, CategoryMemberMeta, SymbolMeta, \
    CategorySymbolMeta, RankBar
import abc
from datetime import datetime
from typing import List, Optional, Dict, Union

from emi.util import NumUtils


class BarSource:

    @abc.abstractmethod
    def get_symbol_list(self) -> List[str]:
        raise NotImplementedError("未实现")

    @abc.abstractmethod
    def get_day_list(self, symbol: str = None,interval:Interval=None, start: datetime = None, end: datetime = None) -> List[datetime]:
        """
        param:
            symbol 允许为None
        """
        raise NotImplementedError("未实现")

    @abc.abstractmethod
    def get_bars(self, symbol: str, interval: Interval = None, start: datetime = None,
                 end: datetime = None,with_detail = True) -> List[BarData]:
        """
        返回股票的历史行情。
        参数：
        :interval 行情频率，为None时取默认值：get_default_interval()
        :start ： 开始时间，包含当天
        :end：   结束时间：包含当天
        """
        raise NotImplementedError("未实现")

    @abc.abstractmethod
    def get_bars_recently(self, symbol: str, time: datetime, count: int, interval: Interval = None, is_before=True,with_detail = True) -> List[BarData]:
        raise NotImplementedError("未实现")

    @abc.abstractmethod
    def get_bars_at_day(self, time: datetime, interval: Interval = None, with_detail=True) -> Dict[str, BarData]:
        raise NotImplementedError("未实现")

class DayDB:

    def refresh_today(self)->datetime:
        """
        刷新当前的天数
        """
        raise NotImplementedError("未实现")


    def get_day(self)->datetime:
        """
        返回当天的最新实时天数
        """
        raise NotImplementedError("未实现")

    def get_interval(self)->Interval:
        """
        返回当天的分钟粒度行情。
        :return:
        """
        raise NotImplementedError("未实现")

    def get_symbol_list(self) -> List[str]:
        raise NotImplementedError("未实现")

    def get_hhmmss_list(self)->List[int]:
        """
        返回当天的hhmmss时间段。
        :param symbol:
        :param interval:
        :param start_hh_mm:
        :param end_hh_mm:
        :return:
        """
        raise NotImplementedError("未实现")

    def get_bars(self, symbol: str) -> List[BarData]:
        """
        返回个股的当天分钟行情。
        :param symbol:
        :param interval:
        :return:
        """
        raise NotImplementedError("未实现")

    def get_bars_at_hhmm(self, hhmm:int) -> List[BarData]:
        """
        返回个股的当天分钟行情。
        :param hhmm:
        :return:
        """
        raise NotImplementedError("未实现")

    def get_tick(self,symbol:str)->Optional[BarData]:
        raise NotImplementedError("未实现")

    def get_ticks(self, symbols: List[str]=None,as_map = False) -> Union[List[BarData],Dict[str,BarData]]:
        """
        返回个当天行情快照。
        :param symbol:
        :param interval:
        :param start: 开始的hhmmss
        :param end: 结束hhmmss
        :return:
        """
        raise NotImplementedError("未实现")

    #
    # def get_bars_by_batch(self, symbols: List[str]) -> Dict[str, List[BarData]]:
    #     """
    #     批量返回个股分钟行情数据
    #     :param symbols:
    #     :return:
    #     """
    #     raise NotImplementedError("未实现")


class BarDB(BarSource):
    """
      行情数据。
      """

    def set_default_interval(self,interval:Interval):
        assert interval is not None
        setattr(self,"_default_interval",interval)

    def get_default_interval(self)->Interval:
        return getattr(self,"_default_interval",Interval.DAILY)



    def get_bars_recently(self, symbol: str, time: datetime, count: int, interval: Interval = None, is_before=True,with_detail = True) -> List[BarData]:
        """
        返回time(包含当天）最近以前的bar行情数据:
        :interval 行情频率，为None时取默认值：get_default_interval()
        """
        pass

    def get_bars_at_day(self, time: datetime, symbols:List[str]=None, interval: Interval = None,with_detail = True) -> Dict[str, BarData]:
        """
        获取某一天的所有数据:
        key : symbol。
        :interval 行情频率，为None时取默认值：get_default_interval()
        """
        raise NotImplementedError("未实现")

    def get_newest_datetime(self, symbol: str = None, interval=Interval.DAILY) -> Optional[datetime]:
        raise NotImplementedError("未实现")

    def get_newest_bar(self, symbol: str = None, interval=Interval.DAILY) -> Optional[BarData]:
        raise NotImplementedError("未实现")

    def get_oldest_datetime(self, symbol: str = None, interval=Interval.DAILY) -> Optional[datetime]:
        raise NotImplementedError("未实现")


    def get_day_db(self, day: datetime, interval = Interval.M1) -> DayDB:
        """
        返回某一天你的行情数据。适合细粒度的行情。
        """
        pass


    def size(self, symbol: str = None) -> int:
        raise NotImplementedError("未实现")


class MarketMeta:
    """
    市场结构数据。包含个股和行业分类结构。
    CategoryMeta：行业分类结构数据对象
    CategorySymbolMeta: 行业分类对象的成员个股信息
    SymbolMeta: 个股结构数据对象
    """

    def get_category_type_name_map(self)->Dict[int,str]:
        """
        返回categy.type对应的名称。
        :return:
        """
        raise NotImplementedError()

    def get_index_metas(self)->List[SymbolMeta]:
        """
        获取大盘指数结构数据 .
        SymbolMeta的具体值指的是：MarketIndex 里面的枚举值
        :return:
        """
        raise NotImplementedError()

    def get_all_category_metas(self,as_map = False, type=None, page_num = 1, page_size = None) -> Union[List[CategoryMeta], Dict[str, CategoryMeta]]:
        """
        返回所有的行业分类。
        :param as_map: 是否返回map对象
        :param type:行业类型：0未定义，  1,表示大盘指数（上证指数，深证），2 表示行业大分类  3、表示行业小分类。
        :param page_num: 从1开始
        :param page_size: 分页尺寸，为None时表示不分页。
        :return:
        """
        raise NotImplementedError()

    def get_all_symbol_metas(self,as_map = False,page_num = 1, page_size = None) -> Union[List[SymbolMeta], Dict[str, SymbolMeta]]:
        """
        返回所有的个股结构信息。
        :param as_map: 是否返回map对象
        :param page_num: 从1开始
        :param page_size: 分页尺寸，为None时表示不分页。
        :return:
        """
        raise NotImplementedError()

    def get_symbol_metas(self,codes:List[str],as_map=False)-> Union[List[SymbolMeta], Dict[str, SymbolMeta]]:
        raise NotImplementedError()

    def get_symbol_meta(self,code:str)-> SymbolMeta:
        raise NotImplementedError()

    def get_all_symbols(self)->List[str]:
        """
        返回所有的个股结构代码。
        """
        raise NotImplementedError()


    def get_category_metas_by_symbol(self,symbol,type = None)->List[CategoryMeta]:
        """
        返回个股所属分类行业信息
        :param symbol:
        :param type:行业类型：0未定义，  1,表示大盘指数（上证指数，深证），2 表示行业大分类  3、表示行业小分类。
        :return:
        """
        raise NotImplementedError()

    def get_category_meta(self, code:Union[str,List[str]],as_map = False) -> Union[CategoryMeta,List[CategoryMeta]]:
        """
        返回分类行业信息
        :param code: 行业分类代码
        :param type:行业类型：0未定义，  1,表示大盘指数（上证指数，深证），2 表示行业大分类  3、表示行业小分类。
        :return:
        """
        raise NotImplementedError()

    def get_category_symbol_metas(self,category_code:str,as_map = False)->Union[List[CategorySymbolMeta],Dict[str,CategorySymbolMeta]]:
        """
        返回分类下的所有个股信息
        :param category_code:
        :return:
        """
        raise NotImplementedError()

    def category_count(self,type = None):
        """
        市场行业分类总数
        :param type:行业类型：0未定义，  1,表示大盘指数（上证指数，深证），2 表示行业大分类  3、表示行业小分类。
        :return:
        """
        raise NotImplementedError()

    def symbol_count(self):
        """
        市场个股总数
        :return:
        """
        raise NotImplementedError()


class RankDB:

    def get_day_list(self)->List[datetime]:
        raise NotImplementedError()


    def get_symbol_list(self)->List[str]:
        raise NotImplementedError()


    def get_rank(self,symbol:str,detail = True,
                 interval:Interval = Interval.DAILY,start:datetime = None,end:datetime = None)->List[RankBar]:
        """
        返回个股的排名情况。
        :param symbol:
        :param detail:是否包含详情数据
        :param start:
        :param end:
        :return:
        """
        raise NotImplementedError()

    def get_rank_recent(self,symbol:str, time: datetime, count: int,detail = True,is_before=True,
                 interval:Interval = Interval.DAILY)->List[RankBar]:
        """
         返回time(包含当天）最近以前的bar行情数据:
        :param symbol:
        :param detail:是否包含详情数据
        :return:
        """
        raise NotImplementedError()


    def get_best(self, time: datetime,detail = True,
                       interval:Interval = Interval.DAILY,max_order: int = None, min_value: float = None,min_count:int = None)->List[RankBar]:
        """
        返回最佳排名值。
        :param time:
        :param detail:
        :param max_order:
        :param min_value:
        :param min_count:
        :return:
        """
        raise NotImplementedError()

    def get_rank_at(self,time,symbol:Union[str,List[str]]=None,detail = True,
                    interval:Interval = Interval.DAILY,as_map=False)->Union[Dict[str,RankBar],List[RankBar]]:
        """
        返回某天的排名各个个股排名情况。
        :param time:
        :param symbol: 为None时表示所有个股。
        :param detail:
        :param interval:
        :return:
        """
        raise NotImplementedError()


    def get_newest_rank(self,symbol:Union[str,List[str]] = None,detail = True,interval:Interval = Interval.DAILY)->List[RankBar]:
        """
        获取最新那天的最新的排名
        :param symbol: 单个或者多个。
        :param detail:
        :return:
        """
        raise NotImplementedError()


    def get_newest_best(self,detail = True,interval:Interval = Interval.DAILY, max_order: int = None, min_value: float = None,min_count:int = None)->List[RankBar]:
        """
        返回最新一天的最优排名情况。
        :param detail:
        :param interval:
        :param max_order:
        :param min_value:
        :param min_count:
        :return:
        """
        raise NotImplementedError()



class RealtimeBarSource(BarSource):
    """
    实时BarSource。


    """

    def __init__(self,bar_db:BarDB,today_db:DayDB):
        self._bar_db = bar_db
        self._today_db = today_db
        self._today_tick_map = {}
        if today_db:
            _today = today_db.get_day()
            if _today:
                self._today_tick_map = today_db.get_ticks(as_map=True)
        cur_time = datetime.now()
        self.T1 = cur_time.replace(hour=9, minute=29)
        self.T2 = cur_time.replace(hour=11, minute=30)
        self.T3 = cur_time.replace(hour=15, minute=00)

    @abc.abstractmethod
    def get_symbol_list(self) -> List[str]:
        return self._bar_db.get_symbol_list()

    @abc.abstractmethod
    def get_day_list(self, symbol: str = None, interval: Interval = None, start: datetime = None,
                     end: datetime = None) -> List[datetime]:
        """
        param:
            symbol 允许为None
        """
        days = self._bar_db.get_day_list(symbol,interval,start,end)
        _today = self._today_db.get_day()
        if _today and days and days[-1].date() < _today.date():
            days.append(_today)
        return days

    def get_bars(self, symbol: str, interval: Interval = None, start: datetime = None,
                 end: datetime = None, with_detail=True) -> List[BarData]:

        bars = self._bar_db.get_bars(symbol,interval,start,end,with_detail)
        assert interval.DAILY
        _today = self._today_db.get_day()
        if _today and bars:
            today_bar = self._today_tick_map.get(symbol)
            pre_bar:BarData = bars[-1]
            if today_bar and pre_bar.datetime.date() <= today_bar.datetime.date():

                if pre_bar.datetime.date() == today_bar.datetime.date():
                    ## 删除同一天的历史行情数据，优先使用实时数据。
                    bars.pop()
                    pre_bar = bars[-1]
                    assert pre_bar.datetime.date() < today_bar.datetime.date()

                ##预估当天的最终成交量、成交额
                bar_time = today_bar.datetime
                bar: BarData = today_bar
                remain_minute = 0
                if bar_time < self.T1:
                    remain_minute = 241
                elif bar_time < self.T2:
                    remain_minute = 121 + int((self.T2 - bar_time).seconds / 60)
                elif bar_time < self.T3:
                    remain_minute = int((self.T3 - bar_time).seconds / 60)

                ##预计收盘成交量
                if remain_minute > 0 and remain_minute < 235:
                    rate = 241 / (241 - remain_minute)
                    bar.volume = bar.volume * rate
                    bar.amount = bar.amount * rate

                ## 复权值。
                bar.high  = bar.high * pre_bar.price_adjust
                bar.low  = bar.low * pre_bar.price_adjust
                bar.open  = bar.open * pre_bar.price_adjust
                bar.close  = bar.close * pre_bar.price_adjust
                bar.pre_close = bar.pre_close * pre_bar.price_adjust
                bar.price_adjust = pre_bar.price_adjust
                if not NumUtils.is_equal(bar.pre_close,pre_bar.close,0.01):
                    report.log_warn(f"{bar.symbol}实时价格复权值跟上一个交易日不一致(很可能是分红日)，pre_close :{bar.pre_close:.3f}, pre_bar.close:{pre_bar.close:.3f}","RealtimeBarSource")
                bars.append(bar)
        return bars